﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BacteriaSimulation.Simulation
{
    public class DetectBacteriaCollisionTask : AbstractTask
    {
        public override TaskOutcome executeTask(Simulation simulation, Bacterium bacterium, SimulationEnvironment env)
        {
            if (env.DetectBacteriaCollision)
            {
                // chek the cell to which the bacterium belongs
                foreach (Bacterium other in bacterium.Cell.Bacteria)
                {
                    if (DetectCollision(bacterium, other))
                    {
                        HandleBacteriaOverlap(bacterium, other, env);
                        bacterium.BacteriumCollisionDetected(other, env);
                        //other.BacteriumCollisionDetected(bact, Env);
                    }
                }

                // check the adjacent cells
                foreach (IndexGridCell cell in simulation.Grid.GetAdjacentCells(bacterium.Cell.RowIndex, bacterium.Cell.ColumnIndex))
                {
                    foreach (Bacterium other in cell.Bacteria)
                    {
                        if (DetectCollision(bacterium, other))
                        {
                            HandleBacteriaOverlap(bacterium, other, env);
                            bacterium.BacteriumCollisionDetected(other, env);
                            //other.BacteriumCollisionDetected(bact, Env);
                        }
                    }
                }

                bacterium.AdjustPosition(env);

                simulation.Grid.Assign(bacterium);
            }

            return new TaskOutcome(TaskOutcomeType.CONTINUE);
        }

        protected void HandleBacteriaOverlap(Bacterium bacterium, Bacterium other, SimulationEnvironment env)
        {
            Double radius1 = bacterium.Width / 2;
            Double radius2 = other.Width / 2;
            // computes the coordinates of the centers of the bacteria circles:
            Double x1 = bacterium.X + radius1;
            Double y1 = bacterium.Y + radius1;
            Double x2 = other.X + radius2;
            Double y2 = other.Y + radius2;
            Double distance = SimulationPoint.Distance(new SimulationPoint(x1, y1), new SimulationPoint(x2, y2));

            Double halfOverlap = ((radius1 + radius1) - distance) / 2;
            double dx1 = (x1 - x2) / distance;
            double dy1 = (y1 - y2) / distance;
            double dx2 = (x2 - x1) / distance;
            double dy2 = (y2 - y1) / distance;

            // new coordinates of the bacteria
            double bact1X = x1 + (halfOverlap * dx1) - radius1;
            double bact1Y = y1 + (halfOverlap * dy1) - radius1;
            double bact2X = x2 + (halfOverlap * dx2) - radius2;
            double bact2Y = y2 + (halfOverlap * dy2) - radius2;
            //Boolean overflow = false;

            if (bact1X < 0 || bact1X > env.Width - radius1 * 2)
            {
                // cannot move bacterium over the edges of the environment
                bact1X = x1;
                //bact2X = x2 + (halfOverlap * 2 * dx2) - radius2;
                //overflow = true;
            }
            if (bact1Y < 0 || bact1Y > env.Height - radius1 * 2)
            {
                bact1Y = y1;
                //bact2Y = y2 + (halfOverlap * 2 * dy2) - radius2;
                //overflow = true;
            }
            if (bact2X < 0 || bact2X > env.Width - radius2 * 2)
            {
                // cannot move bacterium over the edges of the environment
                bact2X = x2;
                //bact1X = x1 + (halfOverlap * 2 * dx1) - radius1;
                //overflow = true;
            }
            if (bact2Y < 0 || bact2Y > env.Height - radius2 * 2)
            {
                bact2Y = y2;
                //bact1Y = y1 + (halfOverlap * 2 * dy1) - radius1;
                //overflow = true;
            }

            bacterium.X = bact1X;
            bacterium.Y = bact1Y;
            other.X = bact2X;
            other.Y = bact2Y;
            //System.Diagnostics.Debug.WriteLineIf(overflow, "bactX1 = " + bact1X + ", bactY1 = " + bact1Y + ", bactX2 = " + bact2X + ", bactY2 = " + bact2Y);
            //System.Diagnostics.Debug.WriteLineIf(overflow, "X1 = " + x1 + ", Y1 = " + y1 + ", X2 = " + x2 + ", Y2 = " + y2);
        }

        public static Boolean DetectCollision(Bacterium bacterium, Bacterium other)
        {
            if (other == bacterium)
            {
                return false;
            }
            SimulationPoint center = new SimulationPoint(bacterium.Position.X + bacterium.Width / 2, bacterium.Position.Y + bacterium.Height / 2);
            SimulationPoint otherCenter = new SimulationPoint(other.Position.X + other.Width / 2, other.Position.Y + other.Height / 2);
            double bacteriumRadius = bacterium.Width / 2;
            double otherBacteriumRadius = other.Width / 2;

            double dx = otherCenter.X - center.X;
            double dy = otherCenter.Y - center.Y;
            bool collision = (dx * dx) + (dy * dy) <= Math.Pow((bacteriumRadius + otherBacteriumRadius), 2);

            return collision;
        }
    }
}
